//
// Created by DELL on 2022/4/24.
//

#pragma once

#include "../Utils.h"
#include "../VulkanDefine.h"
#include "DescriptorSet.h"

BEGIN_NS_VULKAN

class UniformBuffer
{
public:

    ~UniformBuffer();
    UniformBuffer(const UniformBuffer&) = delete;
    UniformBuffer& operator=(const UniformBuffer&) = delete;

    static UniformBufferRef create(DeviceRef& device, const UniformBlock& block);

    const std::string& getName() const { return this->name; }
    int getSize() const { return this->size; }
    int getBinding() const { return this->binding; }

    UniformField getField(const std::string& fieldName)
    {
        return this->fields[fieldName];
    }

    const UniformFieldSet & getAllFields() const { return this->fields; }

    VkBuffer getBuffer() const { return this->buffer; }

    void flush();

    template<class T>
    void setField(const std::string& fieldName, const T& value)
    {
        auto it = this->fields.find(fieldName);
        if (it != this->fields.end())
        {
            memcpy(bufferData.data() + it->second.offset, &value, sizeof(value));
        }
    }

    void setField(const std::string& fieldName, void* param)
    {
        auto it = this->fields.find(fieldName);
        if (it != this->fields.end())
        {
            memcpy(bufferData.data() + it->second.offset, param, UniformFieldTypeSize[it->second.type]);
        }
    }

    void setBuffer(int offset, void* param, int length)
    {
        memcpy(bufferData.data() + offset, param, length);
    }

private:

    explicit UniformBuffer(DeviceRef device);

    void init(const UniformBlock& block);

    std::string name;
    int binding{0};
    int size{0};
    UniformFieldSet fields{0};
    ByteBuffer bufferData;
    void* mapData{};

    DeviceRef device;
    VkBuffer buffer{nullptr};
    VkDeviceMemory memory{nullptr};
};

END_NS_VULKAN
